﻿//-----------------------------------------------------------------------
// <copyright file="ModelBase.cs" company="WpfDevelopment.com">
//     Copyright WpfDevelopment.com. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace WpfDevelopment.MVVM
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.Linq.Expressions;
    using System.Reflection;
    using System.Text;

    /// <summary>
    /// The base class for a model entity
    /// </summary>
    public abstract class ModelBase : IDataErrorInfo, INotifyPropertyChanged
    {
        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Gets or sets the id.
        /// </summary>
        /// <value>The id of this model entity.</value>
        [Key]
        public int Id { get; set; }

        /// <summary>
        /// Gets an error message indicating what is wrong with this object.
        /// </summary>
        /// <value></value>
        /// <returns>An error message indicating what is wrong with this object. The default is an empty string ("").</returns>
        public string Error
        {
            get
            {
                return this[string.Empty];
            }
        }

        /// <summary>
        /// Gets the <see cref="System.String"/> with the specified member name.
        /// </summary>
        /// <param name="columnName">the name of the member to check.</param>
        /// <value>the name of the member to check</value>
        /// <returns>the list of validation errors found</returns>
        public string this[string columnName]
        {
            get
            {
                List<ValidationResult> validationResults = new List<ValidationResult>();

                if (string.IsNullOrEmpty(columnName))
                {
                    Validator.TryValidateObject(this, new ValidationContext(this, null, null), validationResults, true);
                }
                else
                {
                    PropertyInfo property = GetType().GetProperty(columnName);

                    if (property == null)
                    {
                        throw new ArgumentException(
                            string.Format(
                            CultureInfo.CurrentCulture,
                            "The specified member {0} was not found on the instance {1}",
                            columnName,
                            this.GetType()));
                    }

                    Validator.TryValidateProperty(property.GetValue(this, null), new ValidationContext(this, null, null) { MemberName = columnName }, validationResults);
                }

                StringBuilder errorBuilder = new StringBuilder();
                foreach (ValidationResult validationResult in validationResults)
                {
                    errorBuilder.AppendLine(validationResult.ErrorMessage);
                }

                return errorBuilder.ToString();
            }
        }

        /// <summary>
        /// Notifies the system that the property is changed.
        /// </summary>
        /// <typeparam name="TProperty">The Property to Notify</typeparam>
        /// <param name="property">A lambda expression defining the property that changed.</param>
        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Lambda"),
         SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Used to allow (() => PropertyName)")]
        protected void NotifyPropertyChanged<TProperty>(Expression<Func<TProperty>> property)
        {
            if (property == null || this.PropertyChanged == null)
            {
                Debug.WriteLine("property or PropertyChanged is null, PropertyChanged event will not be fired");
                return;
            }

            try
            {
                if (property.Body is MemberExpression)
                {
                    var body = property.Body as MemberExpression;
                    this.PropertyChanged(this, new PropertyChangedEventArgs(body.Member.Name));
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Unable to fire command PropertyChanged on {1}, with exception {0}: ", ex.Message, this.GetType().FullName));
                throw;
            }
        }
    }
}
